﻿using System;
using System.Reflection;
using System.Web;
using System.Web.Compilation;
using System.Web.Configuration;
using EOS.Framework.Data;
using EOS.Framework.Kernel;
using EOS.Framework.Web.Mvc;
using log4net;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using EOS.Framework.Contracts.Web.Routing;

namespace NewsDemo
{
    public class Global : HttpApplication
    {
        private readonly ILog logger = LogManager.GetLogger(typeof(Global));

        /// <summary>
        /// Behandelt das Start Event der Applikation. Hier werden typen für Unity registriert.
        /// </summary>
        /// <param name="sender">Die Quelle des Ergeignisses.</param>
        /// <param name="e">Die <see cref="System.EventArgs"/> instanz, welche Ereignisdaten enthält.</param>
        protected void Application_Start(object sender, EventArgs e)
        {
            log4net.Config.XmlConfigurator.Configure();
            // Configure the MVC Framework.
            MvcConfiguration.Configure("eos-configuration");

            HttpContext app = HttpContext.Current;

            IUnityContainer unityContainer = Unity.Current;

            UnityConfigurationSection unityConfigSection = (UnityConfigurationSection)WebConfigurationManager.GetSection("unity");
            unityConfigSection.Containers["mvcContainer"].Configure(unityContainer);

            RegisterAll(unityContainer, typeof(IWebPart), true);
            RegisterAll(unityContainer, typeof(IWebPage), true);

            FindInterfacesAndImplementations(unityContainer, typeof(IBaseDao), false, typeof(ContainerControlledLifetimeManager));

            IRouter router = Unity.Current.Resolve<IRouter>();

            Route welcomeRoute = new Route("/Default.aspx", typeof(MvcRoutingHandler));
            welcomeRoute.Defaults.Add("webPage", "Welcome");
            welcomeRoute.Defaults.Add("action", "Index");
            welcomeRoute.Constraints.Add("id", "^[1-9]{1}[0-9]*$");

            router.Routes.Add("Welcome", welcomeRoute);

            Route pageRoute = new Route("/[webPage].html", typeof(MvcRoutingHandler));
            pageRoute.Defaults.Add("action", "Index");

            router.Routes.Add("Pages", pageRoute);

            Route actionPageRoute = new Route("/[webPage]/[action].html", typeof(MvcRoutingHandler));
            router.Routes.Add("ActionPages", actionPageRoute); 
        }

        #region Unity Type Registering

        /// <summary>
        /// Sucht alle Interfaces, die von dem angegebenen Interface erben und alle Implementierungen dazu und regisitriert Sie für
        /// Unity.
        /// </summary>
        /// <param name="unity">Referenz zum unity container.</param>
        /// <param name="interfaceType">Typ des Basisinterfaces, nach dessen Untertypen gesucht werden soll.</param>
        /// <param name="useName">Wenn es <c>true</c> ist, wird der Name des Typs beim registrieren an Unity verwendet.</param>
        /// <param name="lifeTimeManagerType">Type des Unity Lifetime Managers.</param>
        private void FindInterfacesAndImplementations(IUnityContainer unityContainer, Type interfaceType, bool useName, Type lifeTimeManagerType)
        {
            if (!interfaceType.IsInterface)
            {
                throw new ArgumentException("interfaceType");
            }

            foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
            {
                try
                {
                    foreach (Type type in assembly.GetTypes())
                    {
                        logger.Info("Checking type " + type.Name + " for registering.");

                        if (type.IsInterface && type.GetInterface(interfaceType.FullName) != null)
                        {
                            RegisterAllFromAssembly(assembly, unityContainer, type, useName, lifeTimeManagerType);
                        }
                    }
                }
                catch
                {
                    logger.Error("Could not load types from assemby " + assembly.FullName);
                }
            }
        }

        /// <summary>
        /// Registriert alle Typen in allen verfügbaren Assemblies, die das Interface vom Type "interfaceType" verwenden.
        /// </summary>
        /// <param name="unity">Referenz zum unity container.</param>
        /// <param name="interfaceType">Typ des Basisinterfaces, nach dessen Untertypen gesucht werden soll.</param>
        /// <param name="useName">Wenn es <c>true</c> ist, wird der Name des Typs beim registrieren an Unity verwendet.</param>
        /// <param name="lifeTimeManagerType">Type des Unity Lifetime Managers.</param>
        private void RegisterAll(IUnityContainer unityContainer, Type interfaceType, bool useName)
        {
            RegisterAll(unityContainer, interfaceType, useName, null);
        }

        /// <summary>
        /// Registriert alle Typen in allen verfügbaren Assemblies, die das Interface vom Type "interfaceType" verwenden.
        /// unter Verwendung des angegebenen LifetimeManagers.
        /// </summary>
        /// <param name="unity">Referenz zum unity container.</param>
        /// <param name="interfaceType">Typ des Basisinterfaces, nach dessen Untertypen gesucht werden soll.</param>
        /// <param name="useName">Wenn es <c>true</c> ist, wird der Name des Typs beim registrieren an Unity verwendet.</param>
        private void RegisterAll(IUnityContainer unityContainer, Type interfaceType, bool useName, Type lifeTimeManagerType)
        {
            if (!interfaceType.IsInterface)
            {
                throw new ArgumentException("interfaceType");
            }

            foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
            {
                RegisterAllFromAssembly(assembly, unityContainer, interfaceType, useName, lifeTimeManagerType);
            }
        }

        /// <summary>
        /// Registriert alle Typen im angegebenen Assembly, die das Interface vom Type "interfaceType" verwenden.
        /// unter Verwendung des angegebenen LifetimeManagers.
        /// </summary>
        /// <param name="assembly">Das Assembly.</param>
        /// <param name="unity">Referenz zum unity container.</param>
        /// <param name="interfaceType">Typ des Basisinterfaces, nach dessen Untertypen gesucht werden soll.</param>
        /// <param name="useName">Wenn es <c>true</c> ist, wird der Name des Typs beim registrieren an Unity verwendet.</param>
        private void RegisterAllFromAssembly(Assembly assembly, IUnityContainer unityContainer, Type interfaceType, bool useName, Type lifeTimeManagerType)
        {
            try
            {
                foreach (Type type in assembly.GetTypes())
                {
                    if (type.GetInterface(interfaceType.FullName) != null)
                    {
                        LifetimeManager lifeTimeManager = null;
                        if (lifeTimeManagerType != null)
                        {
                            lifeTimeManager = (LifetimeManager)Activator.CreateInstance(lifeTimeManagerType);
                        }

                        if (useName)
                        {
                            unityContainer.RegisterType(interfaceType, type, type.Name.ToLower(), lifeTimeManager);
                        }
                        else
                        {
                            unityContainer.RegisterType(interfaceType, type, lifeTimeManager);
                        }
                    }
                }
            }
            catch
            {
                logger.Error("Could not load types from assemby " + assembly.FullName);
            }
        }

        #endregion
    }
}